﻿#include "OCCWidget.h"

#include "ui_MainWindow.h"
#include "ui_OCCWidget.h"

OCCWidget::OCCWidget(QWidget *parent)
    : QWidget(parent), ui(new Ui::OCCWidget), m_mode(Action3d::Nothing), m_pos({0, 0}),
      m_parent(static_cast<MainWindow *>(parent->parentWidget()))
{
    // 注意 m_parent 应该是父窗口的父窗口，因为上级窗口为 centralWidget
    ui->setupUi(this);

    // 逐层级启用鼠标追踪
    this->setMouseTracking(true);
    ui->frame->setMouseTracking(true);
    this->setUpdatesEnabled(false);
    ui->frame->setUpdatesEnabled(false);
}

OCCWidget::~OCCWidget()
{
    delete ui;
}

void OCCWidget::initialize()
{
    // 创建 OpenGL 引擎
    Handle(Aspect_DisplayConnection) theDisp = new Aspect_DisplayConnection();
    Handle(OpenGl_GraphicDriver) H_OpenGL_GraphicDriver = new OpenGl_GraphicDriver(theDisp);

    // 创建视口
    m_hViewer = new V3d_Viewer(H_OpenGL_GraphicDriver);

    // 光照
    Handle(V3d_DirectionalLight) LightDir = new V3d_DirectionalLight(V3d_Zneg, Quantity_Color(Quantity_NOC_GRAY97), 1);
    Handle(V3d_AmbientLight) LightAmb = new V3d_AmbientLight();

    // 设置光照方向
    LightDir->SetDirection(1.0, -2.0, -10.0);

    // 添加光照
    m_hViewer->AddLight(LightDir);
    m_hViewer->AddLight(LightAmb);
    m_hViewer->SetLightOn(LightDir);
    m_hViewer->SetLightOn(LightAmb);

    // 创建交互上下文
    m_hAisContext = new AIS_InteractiveContext(m_hViewer);

    // 配置全局设置，渲染边界
    const Handle(Prs3d_Drawer) &contextDrawer = m_hAisContext->DefaultDrawer();
    if (!contextDrawer.IsNull())
    {
        const Handle(Prs3d_ShadingAspect) &SA = contextDrawer->ShadingAspect();
        const Handle(Graphic3d_AspectFillArea3d) &FA = SA->Aspect();
        contextDrawer->SetFaceBoundaryDraw(true);
        // contextDrawer->SetDeviationCoefficient(1e-5);  // 更高的精度

        FA->SetEdgeOff();

        // 设置最大绘制线路
        contextDrawer->SetMaximalParameterValue(1000);
    }

    // 更新视口
    update_interactive_context();

    // 通过 V3d_Viewer 创建视口
    m_hView = m_hViewer->CreateView();

    // 通过当前窗口句柄创建 WNT_Window，并将视口连接到 WNT_Window
    ui->frame->setAttribute(Qt::WA_PaintOnScreen);
    Handle(WNT_Window) H_WNT_Window = new WNT_Window((Aspect_Handle)ui->frame->winId());
    m_hView->SetWindow(H_WNT_Window);

    // 窗口映射
    if (!H_WNT_Window->IsMapped())
        H_WNT_Window->Map();

    // 设置视口
    m_hView->MustBeResized();
    m_hView->SetShadingModel(V3d_PHONG);

    // 设置白色背景
    Quantity_Color bgColor(1.0, 1.0, 1.0, Quantity_TOC_RGB);
    m_hView->SetBackgroundColor(bgColor);

    // 配置渲染参数，例如反走样、阴影等
    Graphic3d_RenderingParams &RenderParams = m_hView->ChangeRenderingParams();
    RenderParams.IsAntialiasingEnabled = true;
    RenderParams.NbMsaaSamples = 8;
    RenderParams.IsShadowEnabled = false;
    RenderParams.CollectedStats = Graphic3d_RenderingParams::PerfCounters_NONE;
}

void OCCWidget::update_interactive_context()
{
    // 移除所有物体
    m_hAisContext->RemoveAll(false);

    // 遍历每个形状绘制，并激活选择风格
    for (auto sh : m_parent->m_shapes)
    {
        Handle(AIS_Shape) shape = new AIS_Shape(sh);
        shape->SetColor(Quantity_NOC_GREEN);

        // 使用 false 会快很多
        m_hAisContext->Display(shape, false);
        m_hAisContext->SetDisplayMode(shape, AIS_Shaded, false);
    }

    for (auto sh : m_parent->m_selected)
    {
        TopExp_Explorer Ex;
        for (Ex.Init(sh, TopAbs_EDGE); Ex.More(); Ex.Next())
        {
            Handle(AIS_Shape) shape = new AIS_Shape(TopoDS::Edge(Ex.Current()));
            shape->SetColor(Quantity_NOC_RED);

            // 使用 false 会快很多
            m_hAisContext->Display(shape, false);
            m_hAisContext->SetDisplayMode(shape, AIS_Shaded, false);
        }

        for (Ex.Init(sh, TopAbs_FACE); Ex.More(); Ex.Next())
        {
            Handle(AIS_Shape) shape = new AIS_Shape(TopoDS::Face(Ex.Current()));
            shape->SetColor(Quantity_NOC_GREEN);

            Handle(Prs3d_LineAspect) aBoundaryAspect =
                new Prs3d_LineAspect(Quantity_NOC_BLUE, Aspect_TOL_SOLID, 3.0); // 蓝色边界，线宽 1.0
            shape->Attributes()->SetFaceBoundaryAspect(aBoundaryAspect);

            // 使用 false 会快很多
            m_hAisContext->Display(shape, false);
            m_hAisContext->SetDisplayMode(shape, AIS_Shaded, false);
        }
    }

    // 绘制点云
    for (auto cloud : m_parent->m_clouds)
    {
        cloud->SetColor(Quantity_NOC_BLUE);
        cloud->Attributes()->ShadingAspect()->Aspect()->SetMarkerScale(5);
        m_hAisContext->Display(cloud, false);
    }

    // 修改选择风格
    adjust_selection_style(m_hAisContext);

    // 激活选择
    m_hAisContext->Activate(8, true); // triangle
    m_hAisContext->Activate(4, true); // faces
    m_hAisContext->Activate(2, true); // edges
    // m_hAisContext->Activate(1, true);  // vertex
}

void OCCWidget::adjust_selection_style(const Handle(AIS_InteractiveContext) & context)
{
    // 设置渲染信息以及选中物体的高亮信息
    Handle(Prs3d_Drawer) selDrawer = new Prs3d_Drawer;

    selDrawer->SetLink(context->DefaultDrawer());
    selDrawer->SetFaceBoundaryDraw(true);
    selDrawer->SetDisplayMode(1);
    selDrawer->SetTransparency(1.0f);
    selDrawer->SetZLayer(Graphic3d_ZLayerId_Topmost);
    selDrawer->SetColor(Quantity_NOC_BLUE);
    selDrawer->SetBasicFillAreaAspect(new Graphic3d_AspectFillArea3d());

    // 调整填充区域颜色材质等信息
    const Handle(Graphic3d_AspectFillArea3d) &fillArea = selDrawer->BasicFillAreaAspect();

    fillArea->SetInteriorColor(Quantity_NOC_GREEN);
    fillArea->SetBackInteriorColor(Quantity_NOC_GREEN);

    fillArea->ChangeFrontMaterial().SetMaterialName(Graphic3d_NOM_NEON_GNC);
    fillArea->ChangeFrontMaterial().SetTransparency(1.0f);
    fillArea->ChangeBackMaterial().SetMaterialName(Graphic3d_NOM_NEON_GNC);
    fillArea->ChangeBackMaterial().SetTransparency(1.0f);

    selDrawer->UnFreeBoundaryAspect()->SetWidth(1.0);

    // 更新 AIS context
    context->SetHighlightStyle(Prs3d_TypeOfHighlight_LocalSelected, selDrawer);
}

void OCCWidget::fit_all_shapes()
{
    // 存在物体则适应物体
    auto window = static_cast<MainWindow *>(parentWidget());
    if (!m_parent->m_shapes.empty() || !m_parent->m_clouds.empty() || !m_parent->m_triangulations.empty())
        m_hView->FitAll();
}

std::vector<TopoDS_Shape> OCCWidget::selected_shapes()
{
    std::vector<TopoDS_Shape> sList;
    for (m_hAisContext->InitSelected(); m_hAisContext->MoreSelected(); m_hAisContext->NextSelected())
    {
        Handle(SelectMgr_EntityOwner) anOwner = m_hAisContext->SelectedOwner();
        Handle(AIS_InteractiveObject) anObj = Handle(AIS_InteractiveObject)::DownCast(anOwner->Selectable());

        // 将 SelectMgr_EntityOwner 对象下降为 StdSelect_BRepOwner 对象
        if (Handle(StdSelect_BRepOwner) aBRepOwner = Handle(StdSelect_BRepOwner)::DownCast(anOwner))
        {
            TopoDS_Shape aShape = aBRepOwner->Shape();
            sList.push_back(aShape);
        }
    }
    return sList;
}

Handle(AIS_PointCloud) OCCWidget::selected_point_cloud()
{
    for (m_hAisContext->InitSelected(); m_hAisContext->MoreSelected(); m_hAisContext->NextSelected())
    {
        Handle(SelectMgr_EntityOwner) anOwner = m_hAisContext->SelectedOwner();
        Handle(AIS_PointCloud) pointCloud = Handle(AIS_PointCloud)::DownCast(anOwner->Selectable());
        if (!pointCloud.IsNull())
            return pointCloud;
    }
    return Handle(AIS_PointCloud)();
}

void OCCWidget::resizeEvent(QResizeEvent *)
{
    ui->frame->resize(size());
    qDebug() << size();

    // 重绘视口
    if (m_hView.IsNull() == FALSE)
        m_hView->MustBeResized();
}

void OCCWidget::paintEvent(QPaintEvent *event)
{
    // 重绘视口
    if (m_hView.IsNull() == FALSE)
    {
        m_hView->MustBeResized();
        m_hView->Redraw();
    }
}

void OCCWidget::mousePressEvent(QMouseEvent *event)
{
    QPoint pos = event->pos();

    if (event->button() == Qt::LeftButton)
    {
        Handle(AIS_InteractiveContext) ctx = m_hAisContext;
        ctx->Select(Standard_True);

        // 处理选中事件
        m_parent->ctrl_widget()->deal_with_event();
    }
    else if (event->button() == Qt::MiddleButton)
    {
        // 如果未选中，则进入旋转模式
        m_mode = Action3d::Rotation;
        m_hView->StartRotation(pos.x(), pos.y());
    }
    else if (event->button() == Qt::RightButton)
    {
        // 若选中，则进入平移模式
        m_mode = Action3d::Panning;
        m_pos = pos;
    }
}

void OCCWidget::mouseReleaseEvent(QMouseEvent *event)
{
    // 松开鼠标退出模式
    m_mode = Action3d::Nothing;
}

void OCCWidget::mouseMoveEvent(QMouseEvent *event)
{
    QPoint pos = event->pos();

    switch (m_mode)
    {
    case Action3d::Nothing: {
        // 判断是否选中
        Handle(AIS_InteractiveContext) ctx = m_hAisContext;
        ctx->MoveTo(pos.x(), pos.y(), m_hView, Standard_True);
        break;
    }
    case Action3d::Panning: {
        // 平移视角
        m_hView->Pan(pos.x() - m_pos.x(), -pos.y() + m_pos.y());
        m_pos = pos;
        break;
    }
    case Action3d::Rotation: {
        // 旋转视角
        m_hView->Rotation(pos.x(), pos.y());
        break;
    }
    }

    // 鼠标移动就刷新
    m_hView->Update();
}

void OCCWidget::wheelEvent(QWheelEvent *event)
{
    QPoint delta = event->angleDelta();

    if (m_mode == Action3d::Nothing)
    {
        // 缩放视口
        if (delta.y() > 0)
        {
            double scar = 1.2;
            if (m_hView->Scale() <= 10000)
                m_hView->SetZoom(scar);
        }
        else
        {
            double scar = 0.8;
            if (m_hView->Scale() > 0.0001)
                m_hView->SetZoom(scar);
        }
    }
}

QPaintEngine *OCCWidget::paintEngine() const
{
    return nullptr;
}